home *** CD-ROM | disk | FTP | other *** search
/ PC Format (South-Africa) 2001 June / PCFJune.iso / mweb / MWEB Utils / ws295sdk.exe / Ws2sdkzp.exe / SAMPLES / WS2CHAT / WS2CHAT.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-06-06  |  48.3 KB  |  1,717 lines

  1. /*++
  2.  
  3. Copyright (c) 1995 Intel Corp
  4.  
  5. Module Name:
  6.  
  7.     ws2chat.c
  8.  
  9. Abstract:
  10.  
  11.     Contains WinMain and other user interface code for the WinSock2
  12.     Chat sample application.
  13.  
  14. --*/
  15.  
  16.  
  17. #include "nowarn.h"  /* turn off benign warnings */
  18. #ifndef _WINSOCKAPI_
  19. #define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
  20. #endif
  21. #include <windows.h>
  22. #include <winsock2.h>
  23.  
  24. #include "nowarn.h"  /* some warnings may have been turned back on */
  25. #include <stdlib.h>
  26. #include "ws2chat.h"
  27. #include "chatsock.h"
  28. #include "chatdlg.h"
  29. #include "queue.h"
  30.  
  31.  
  32. //
  33. // Forward References -- internal functions.
  34. //
  35.  
  36. // Startup/Initialization Functions
  37. int PASCAL
  38. WinMain(
  39.     IN HINSTANCE InstanceHandle,
  40.     IN HINSTANCE PrevInstanceHandle,
  41.     IN LPSTR  CmdLine,
  42.     IN int    CmdShow);
  43.  
  44. BOOL
  45. InitUI(
  46.     IN HINSTANCE InstanceHandle,
  47.     IN HINSTANCE PrevInstanceHandle);
  48.  
  49. long CALLBACK
  50. MainWndProc(
  51.     IN HWND   WindowHandle,
  52.     IN UINT   Message,
  53.     IN WPARAM WParam,
  54.     IN LPARAM LParam);
  55.  
  56. long CALLBACK
  57. ConnWndProc(
  58.     IN HWND   WindowHandle,
  59.     IN UINT   Message,
  60.     IN WPARAM WParam,
  61.     IN LPARAM LParam);
  62.  
  63. long CALLBACK
  64. SendEditSubClass(
  65.     IN HWND   WindowHandle,
  66.     IN UINT   Message,
  67.     IN WPARAM WParam,
  68.     IN LPARAM LParam);
  69.  
  70. long CALLBACK
  71. RecvEditSubClass(
  72.     IN HWND   WindowHandle,
  73.     IN UINT   Message,
  74.     IN WPARAM WParam,
  75.     IN LPARAM LParam);
  76.  
  77. BOOL CALLBACK
  78. CloseEnumProc(
  79.     IN HWND WindowHandle,
  80.     IN LONG LParam);
  81.  
  82. BOOL
  83. GetConnectionInfo(
  84.     IN HWND ConnectionWindow);
  85.  
  86. void
  87. QueryAndMaximize(void);
  88.  
  89.  
  90. //
  91. // Static Global Variables
  92. //
  93.  
  94. static HMENU   MainMenu;       // the main MDI menu
  95. static HMENU   MainMenuWindow; // the window associated with the above
  96. static FARPROC OldEditWndProc; // edit control WndProc before subclassing
  97.  
  98.  
  99.  
  100. //
  101. // Externally-Visible Global Variables
  102. //
  103.  
  104. HANDLE  GlobalInstance;                  // ientifies the instance of chat
  105. char    ConnClassStr[] = "ConnChild";    // string to register window class
  106. char    ChatClassStr[] = "WS2ChatFrame"; // string to register window class
  107. HWND    GlobalFrameWindow;               // Chat's main (frame) window
  108.  
  109.  
  110.  
  111. //
  112. // Function Definitions
  113. //
  114.  
  115.  
  116. int CALLBACK
  117. WinMain(
  118.     IN HINSTANCE InstanceHandle,
  119.     IN HINSTANCE PrevInstanceHandle,
  120.     IN LPSTR  CmdLine,
  121.     IN int    CmdShow)
  122.  
  123. /*++
  124.  
  125. Routine Description:
  126.  
  127.     WinMain is the main entry point for WinSock 2 Chat.  This function
  128.     calls initialization functions and goes into a message loop.
  129.  
  130. Arguments:
  131.  
  132.     InstanceHandle -- Supplies the current instance handle.
  133.  
  134.     PrevInstanceHandle -- Supplies the previous instance handle.
  135.  
  136.     CmdLine -- Supplies the address of the command line.
  137.  
  138.     CmdShow -- Supplies the show state of the window.
  139.  
  140. Return Value:
  141.  
  142.     0 -- Initialization failure
  143.  
  144.     !0 -- Returns the Message.wParam of the WM_QUIT message that ended
  145.     the message loop.
  146.  
  147. --*/
  148. {
  149.     HANDLE   AccelTable;   // handle to the accelerator table
  150.     HWND     ClientWindow; // MDI Client window
  151.     MSG      Message;      // holds the message
  152.     int      ReturnValue;  // return value
  153.  
  154.     ReturnValue = 0;
  155.     GlobalInstance = InstanceHandle;
  156.  
  157.     // Initialize Chat.
  158.     if (!InitUI(InstanceHandle, PrevInstanceHandle)) {
  159.         MessageBox(NULL, "Couldn't Initialize Chat!", "Error",
  160.                    MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  161.         goto Done;
  162.     }
  163.  
  164.     // Create the frame window.
  165.     GlobalFrameWindow = CreateWindow(ChatClassStr,
  166.                                      "WinSock 2 Chat",
  167.                                      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  168.                                      CW_USEDEFAULT,
  169.                                      CW_USEDEFAULT,
  170.                                      CW_USEDEFAULT,
  171.                                      CW_USEDEFAULT,
  172.                                      NULL,
  173.                                      (HMENU)MainMenu,
  174.                                      InstanceHandle,
  175.                                      NULL);
  176.  
  177.     if (GlobalFrameWindow == NULL) {
  178.         ChatSysError("CreateWindow()",
  179.                      "WinMain()",
  180.                      TRUE);
  181.     }
  182.  
  183.     // Startup WinSock 2.
  184.     if (!InitWS2()) {
  185.         goto Done;
  186.     }
  187.  
  188.     // Find out about protocols installed.
  189.     if (!FindProtocols()) {
  190.         goto Done;
  191.     }
  192.  
  193.     // Listen on a local address for all installed protocols.
  194.     if (!ListenAll()) {
  195.         goto Done;
  196.     }
  197.  
  198.     // Load some handles, update the application window, and enter the
  199.     // main message loop.
  200.     ClientWindow = GetWindow(GlobalFrameWindow, GW_CHILD);
  201.     AccelTable = LoadAccelerators(InstanceHandle, "MdiAccel");
  202.     if (AccelTable == NULL) {
  203.         ChatSysError("LoadAccelerators()",
  204.                      "WinMain()",
  205.                      FALSE);
  206.     }
  207.     ShowWindow(GlobalFrameWindow, CmdShow);
  208.     UpdateWindow(GlobalFrameWindow);
  209.  
  210.     // Enter the GetMessage loop.
  211.     while (GetMessage(&Message, NULL, 0, 0)) {
  212.         if (!TranslateMDISysAccel(ClientWindow, &Message) &&
  213.             !TranslateAccelerator(GlobalFrameWindow, AccelTable, &Message)) {
  214.              TranslateMessage(&Message);
  215.              DispatchMessage(&Message);
  216.         }
  217.     }
  218.  
  219.     // WM_QUIT was received.  Cleanup and return.
  220.     WSACleanup();
  221.     ReturnValue = Message.wParam;
  222.  
  223.  Done:
  224.  
  225.     return(ReturnValue);
  226.  
  227. } // WinMain()
  228.  
  229.  
  230.  
  231.  
  232.  
  233. BOOL
  234. InitUI(
  235.     IN HINSTANCE InstanceHandle,
  236.     IN HINSTANCE PrevInstanceHandle)
  237.  
  238. /*++
  239.  
  240. Routine Description:
  241.  
  242.     Initializes chat's user interface.  Registers the window classes
  243.     used by chat and obtains handles to chat's menus.
  244.  
  245. Arguments:
  246.  
  247.     InstanceHandle - Supplies the current instance handle.
  248.  
  249.     PrevInstanceHandle - Supplies the previous instance handle.
  250.  
  251. Return Value:
  252.  
  253.     TRUE - Chat successfully initialized
  254.  
  255.     FALSE - Failure during chat initialization
  256.  
  257. --*/
  258. {
  259.     WNDCLASS WndClass;           // window class structure
  260.     BOOL     ReturnValue = TRUE; // holds return value
  261.  
  262.     // If this is the first instance of chat, register the window
  263.     // classes.
  264.     if (!PrevInstanceHandle) {
  265.         WndClass.style         = CS_HREDRAW | CS_VREDRAW;
  266.         WndClass.lpfnWndProc   = MainWndProc;
  267.         WndClass.cbClsExtra    = 0;
  268.         WndClass.cbWndExtra    = 0;
  269.         WndClass.hInstance     = InstanceHandle;
  270.         WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  271.         WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  272.         WndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
  273.         WndClass.lpszMenuName  = NULL;
  274.         WndClass.lpszClassName = ChatClassStr;
  275.         if (!RegisterClass(&WndClass)) {
  276.             ReturnValue = FALSE;
  277.             goto Done;
  278.         }
  279.  
  280.         WndClass.style         = CS_HREDRAW | CS_VREDRAW;
  281.         WndClass.lpfnWndProc   = ConnWndProc;
  282.         WndClass.cbClsExtra    = 0;
  283.         WndClass.cbWndExtra    = CONN_WND_EXTRA;
  284.         WndClass.hInstance     = InstanceHandle;
  285.         WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  286.         WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  287.         WndClass.lpszMenuName  = NULL;
  288.         WndClass.lpszClassName = ConnClassStr;
  289.         if (!RegisterClass(&WndClass)) {
  290.             ReturnValue = FALSE;
  291.             goto Done;
  292.         }
  293.     }
  294.  
  295.     // Obtain handles to the menus & submenus.
  296.     MainMenu  = LoadMenu(GlobalInstance, "MdiMenuMain");
  297.     if (MainMenu == NULL) {
  298.         ChatSysError("LoadMenu()",
  299.                      "InitUI()",
  300.                      TRUE);
  301.     }
  302.     MainMenuWindow  = GetSubMenu(MainMenu, MAIN_MENU_POS);
  303.  
  304.  Done:
  305.  
  306.     return(ReturnValue);
  307.  
  308. } // InitUI()
  309.  
  310.  
  311.  
  312.  
  313.  
  314. long CALLBACK
  315. MainWndProc(
  316.     IN HWND   WindowHandle,
  317.     IN UINT   Message,
  318.     IN WPARAM WParam,
  319.     IN LPARAM LParam)
  320.  
  321. /*++
  322.  
  323. Routine Description:
  324.  
  325.     WinSock 2 chat's main window procedure.
  326.  
  327. Arguments:
  328.  
  329.     WindowHandle - Supplies the window handle for chat's frame window.
  330.  
  331.     Message - Supplies the message identifier.
  332.  
  333.     WParam - Supplies the first message parameter.
  334.  
  335.     LParam - Supplies the second message parameter.
  336.  
  337. Return Value:
  338.  
  339.     Return value depends on the message sent.
  340.  
  341. --*/
  342. {
  343.     static HWND        ClientWindow; // the MDI client window
  344.     CLIENTCREATESTRUCT ClientCreate; // MDI Client window creation data
  345.     HWND               ChildWindow;  // gets handle to child conn. windows
  346.     MDICREATESTRUCT    MdiCreate;    // MDI Child window creation data
  347.  
  348.     switch (Message) {
  349.  
  350.     case WM_CREATE:
  351.  
  352.         // Create the client window.
  353.         ClientCreate.hWindowMenu  = MainMenuWindow;
  354.         ClientCreate.idFirstChild = IDM_FIRSTCHILD;
  355.         ClientWindow = CreateWindow("MDICLIENT",
  356.                                     NULL,
  357.                                     WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
  358.                                     0, 0, 0, 0,
  359.                                     WindowHandle,
  360.                                     (HMENU)CLIENT_WINDOW_ID,
  361.                                     GlobalInstance,
  362.                                     (LPSTR)&ClientCreate);
  363.         if (ClientWindow == NULL) {
  364.             ChatSysError("CreateWindow()",
  365.                          "MainWndProc()",
  366.                          TRUE);
  367.  
  368.         }
  369.  
  370.         return (0);
  371.  
  372.     case WM_COMMAND:
  373.  
  374.         // We know the wm_command is coming from a menu item, so we
  375.         // can safely assume that the high-order word of WParam is
  376.         // zero.
  377.         switch (WParam) {
  378.  
  379.         case IDM_CONNECT:
  380.  
  381.             // Create a Connection child window.
  382.             MdiCreate.szClass = ConnClassStr;
  383.             MdiCreate.szTitle = "Connecting...";
  384.             MdiCreate.hOwner  = GlobalInstance;
  385.             MdiCreate.x       = CW_USEDEFAULT;
  386.             MdiCreate.y       = CW_USEDEFAULT;
  387.             MdiCreate.cx      = CW_USEDEFAULT;
  388.             MdiCreate.cy      = CW_USEDEFAULT;
  389.             MdiCreate.style   = 0;
  390.             MdiCreate.lParam  = 0;
  391.             ChildWindow =
  392.               (HWND) SendMessage(ClientWindow, WM_MDICREATE, 0,
  393.                                  (LPARAM)(LPMDICREATESTRUCT)&MdiCreate);
  394.  
  395.             if (ChildWindow == NULL) {
  396.                 ChatSysError("SendMessage(WM_MDICREATE)",
  397.                              "MainWndProc()",
  398.                              TRUE);
  399.             }
  400.  
  401.             // Get the connection information from the caller and
  402.             // attempt to make a connection...
  403.             if (!GetConnectionInfo(ChildWindow) ||
  404.                 !MakeConnection(ChildWindow)) {
  405.                 DestroyWindow(ChildWindow);
  406.             }
  407.  
  408.             break;
  409.  
  410.         case IDM_CLOSE:
  411.  
  412.             // Close the active window.
  413.             ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDIGETACTIVE, 0,
  414.                                             0);
  415.             SendMessage(ClientWindow, WM_MDIDESTROY, (WPARAM)ChildWindow, 0);
  416.             break;
  417.  
  418.  
  419.         case IDM_EXIT:
  420.  
  421.             // Exit the program.
  422.             SendMessage(WindowHandle, WM_CLOSE, 0, 0);
  423.             break;
  424.  
  425.         case IDM_TILE:
  426.  
  427.             SendMessage(ClientWindow, WM_MDITILE, 0, 0);
  428.             break;
  429.  
  430.         case IDM_CASCADE:
  431.  
  432.             SendMessage(ClientWindow, WM_MDICASCADE, 0, 0);
  433.             break;
  434.  
  435.         case IDM_ARRANGE:
  436.  
  437.             SendMessage(ClientWindow, WM_MDIICONARRANGE, 0, 0);
  438.             break;
  439.  
  440.         case IDM_CLOSEALL:
  441.  
  442.             // Close all child windows.
  443.             EnumChildWindows(ClientWindow, CloseEnumProc, 0);
  444.             break;
  445.  
  446.         default:
  447.  
  448.             // Pass to active child.
  449.             ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDIGETACTIVE,
  450.                                             0, 0);
  451.  
  452.             if (IsWindow(ChildWindow)) {
  453.                 SendMessage(ChildWindow, WM_COMMAND, WParam, LParam);
  454.             }
  455.  
  456.             // This causes us to drop out of the switch and to pass
  457.             // the message on to DefFrameProc().
  458.             break;
  459.  
  460.         } // switch (WParam)
  461.         break;
  462.  
  463.     case WM_CLOSE:
  464.  
  465.         // Close all child windows.
  466.         SendMessage(WindowHandle, WM_COMMAND, IDM_CLOSEALL, 0);
  467.         if (GetWindow(ClientWindow, GW_CHILD) != NULL) {
  468.             return (0);
  469.         }
  470.         break;
  471.  
  472.     case USMSG_ACCEPT:
  473.  
  474.         // Create a MDI Child connection window (even though we aren't
  475.         // yet sure if we will accept the connection -- if we don't,
  476.         // it will be immediately destroyed.)
  477.         MdiCreate.szClass = ConnClassStr;
  478.         MdiCreate.szTitle = "Connection Request...";
  479.         MdiCreate.hOwner  = GlobalInstance;
  480.         MdiCreate.x       = CW_USEDEFAULT;
  481.         MdiCreate.y       = CW_USEDEFAULT;
  482.         MdiCreate.cx      = CW_USEDEFAULT;
  483.         MdiCreate.cy      = CW_USEDEFAULT;
  484.         MdiCreate.style   = 0;
  485.         ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDICREATE, 0,
  486.                                         (LPARAM)(LPMDICREATESTRUCT)&MdiCreate);
  487.         if (ChildWindow == NULL) {
  488.             ChatSysError("SendMessage(WM_MDICREATE)",
  489.                          "MainWndProc()",
  490.                          TRUE);
  491.         }
  492.  
  493.         // Service the incoming connection request.
  494.         HandleAcceptMessage(ChildWindow, (SOCKET)WParam, LParam);
  495.         break;
  496.  
  497.     case WM_DESTROY:
  498.  
  499.         // Clean up the listening sockets and kill the application.
  500.         CleanUpSockets();
  501.         PostQuitMessage(0);
  502.         break;
  503.  
  504.     } // switch(Message)
  505.  
  506.     // Pass unprocessed messages to DefFrameProc.
  507.     return (DefFrameProc(WindowHandle,
  508.                          ClientWindow,
  509.                          Message,
  510.                          WParam,
  511.                          LParam));
  512.  
  513. } // MainWndProc()
  514.  
  515.  
  516.  
  517.  
  518.  
  519. long CALLBACK
  520. ConnWndProc(
  521.     IN HWND   WindowHandle,
  522.     IN UINT   Message,
  523.     IN WPARAM WParam,
  524.     IN LPARAM LParam)
  525.  
  526. /*++
  527.  
  528. Routine Description:
  529.  
  530.     Windows procedure for each connection window.
  531.  
  532. Arguments:
  533.  
  534.     WindowHandle - Supplies the handle to the connection window which
  535.     is to receive the message.
  536.  
  537.     Message - Supplies the message identifier.
  538.  
  539.     WParam - Supplies the first message parameter.
  540.  
  541.     LParam - Supplies the second message parameter.
  542.  
  543. Return Value:
  544.  
  545.     Return value depends on the message sent.
  546.  
  547. --*/
  548. {
  549.     PCONNDATA         ConnData;    // pointer to data for this connection
  550.     RECT              Rect;        // used for painting the window
  551.     HDC               Hdc;         // device context for painting
  552.  
  553.     switch (Message) {
  554.  
  555.     case WM_CREATE:
  556.  
  557.         // Allocate memory for private window data.
  558.         ConnData = (PCONNDATA)malloc(sizeof(CONNDATA));
  559.         if (ConnData == NULL) {
  560.             ChatSysError("malloc()",
  561.                          "ConnWndProc()",
  562.                          TRUE);
  563.         }
  564.         memset((char *)ConnData, 0, sizeof(CONNDATA));
  565.         // Let this to be filled by a getsockopt call later
  566.         ConnData->MaxMsgSize = 0x01;
  567.  
  568.         // Zero is a valid socket ID so set the socket member to INVALID_SOCKET
  569.         ConnData->Socket = INVALID_SOCKET;
  570.  
  571.         GetClientRect(WindowHandle, &Rect);
  572.  
  573.         // Create Send/Recv edit controls.
  574.         ConnData->SendWindow =
  575.           CreateWindow("EDIT",
  576.                        NULL,
  577.                        WS_CHILD | WS_VISIBLE   | WS_VSCROLL |
  578.                        ES_LEFT  | ES_MULTILINE | ES_AUTOVSCROLL,
  579.                        0,
  580.                        0,
  581.                        Rect.right,
  582.                        Rect.bottom/2,
  583.                        WindowHandle,
  584.                        (HMENU)EC_SEND_CHILD,
  585.                        GlobalInstance,
  586.                        NULL);
  587.  
  588.         ConnData->RecvWindow =
  589.           CreateWindow("EDIT",
  590.                        NULL,
  591.                        WS_CHILD | WS_VISIBLE   | WS_VSCROLL |
  592.                        ES_LEFT  | ES_MULTILINE | ES_AUTOVSCROLL,
  593.                        0,
  594.                        Rect.bottom/2 + 1,
  595.                        Rect.right,
  596.                        Rect.bottom - Rect.bottom/2 - 1,
  597.                        WindowHandle,
  598.                        (HMENU)EC_RECV_CHILD,
  599.                        GlobalInstance,
  600.                        NULL);
  601.  
  602.         if (!ConnData->SendWindow || !ConnData->RecvWindow) {
  603.             ChatSysError("CreateWindow()",
  604.                          "ConnWndProc()",
  605.                          TRUE);
  606.         }
  607.  
  608.         // Fill in some connection-specific data.
  609.         ConnData->SocketEventObject = CreateEvent(NULL,
  610.                                                   FALSE,
  611.                                                   FALSE,
  612.                                                   NULL);
  613.         ConnData->OutputEventObject = CreateEvent(NULL,
  614.                                                   FALSE,
  615.                                                   FALSE,
  616.                                                   NULL);
  617.         ConnData->OutputQueue = QCreate();
  618.         if (!ConnData->SocketEventObject || !ConnData->OutputEventObject ||
  619.             !ConnData->OutputQueue) {
  620.             ChatSysError("CreateEvent() or QCreate()",
  621.                          "ConnWndProc()",
  622.                          TRUE);
  623.         }
  624.         ConnData->ConnectionWindow = WindowHandle;
  625.         ConnData->WriteOk = FALSE;
  626.  
  627.         // Subclass the edit controls. Both functions necessarily
  628.         // return the same FARPROC...
  629.         OldEditWndProc = (FARPROC)SetWindowLong(ConnData->SendWindow,
  630.                                                 GWL_WNDPROC,
  631.                                                 (DWORD)SendEditSubClass);
  632.         SetWindowLong(ConnData->RecvWindow,
  633.                       GWL_WNDPROC,
  634.                       (DWORD)RecvEditSubClass);
  635.  
  636.         // Split the connection window in half.
  637.         Hdc = GetDC(WindowHandle);
  638.         MoveToEx(Hdc, 0, Rect.bottom/2, NULL);
  639.         LineTo(Hdc, Rect.right, Rect.bottom/2);
  640.         ReleaseDC(WindowHandle, Hdc);
  641.  
  642.         // Set the private window data to be a pointer to ConnData.
  643.         SetWindowLong(WindowHandle, GWL_CONNINFO, (LONG)ConnData);
  644.  
  645.         return (0);
  646.  
  647.     case WM_COMMAND:
  648.  
  649.         switch (HIWORD(WParam)) {
  650.  
  651.         case 0:
  652.  
  653.             // If the high-order word is zero, this message is being
  654.             // sent from a menu-item (hopefully it was passed to us
  655.             // from the frame window, since the mdi child connection
  656.             // windows, of course, have no menus).
  657.             switch (LOWORD(WParam)) {
  658.  
  659.             case IDM_CLEAR_SENDBUFFER:
  660.  
  661.                 // Clear the send buffer.
  662.                 ConnData = GetConnData(WindowHandle);
  663.                 PostMessage(ConnData->SendWindow, EM_SETSEL, 0, -1);
  664.                 PostMessage(ConnData->SendWindow, WM_CLEAR, 0, 0);
  665.                 return (0);
  666.  
  667.             case IDM_CLEAR_RECVBUFFER:
  668.  
  669.                 // Clear the receive buffer.
  670.                 ConnData = GetConnData(WindowHandle);
  671.                 PostMessage(ConnData->RecvWindow, EM_SETSEL, 0, -1);
  672.                 PostMessage(ConnData->RecvWindow, WM_CLEAR, 0, 0);
  673.                 return (0);
  674.  
  675.             default:
  676.  
  677.                 // An unknown menu item... just return.
  678.                 return (0);
  679.  
  680.             } // switch (LOWORD(WParam))
  681.  
  682.         case 1:
  683.  
  684.             // The high-order word is 1, so thus the wm_command
  685.             // message is coming from an accelerator. Ignore it.
  686.             return(0);
  687.  
  688.         default:
  689.  
  690.             // Let the default window procedure handle it (below).
  691.             break;
  692.  
  693.         } // switch (HIWORD(WParam))
  694.         break;
  695.  
  696.  
  697.     case WM_PAINT:
  698.  
  699.         // Window needs to be painted, split the connection window in
  700.         // half.
  701.         GetClientRect(WindowHandle, &Rect);
  702.         Hdc = GetDC(WindowHandle);
  703.         MoveToEx(Hdc, 0, Rect.bottom/2, NULL);
  704.         LineTo(Hdc, Rect.right, Rect.bottom/2);
  705.         ReleaseDC(WindowHandle, Hdc);
  706.  
  707.         return (DefMDIChildProc(WindowHandle,
  708.                                 Message,
  709.                                 WParam,
  710.                                 LParam));
  711.  
  712.     case WM_SIZE:
  713.  
  714.         ConnData = GetConnData(WindowHandle);
  715.  
  716.         // Resize the edit control windows.
  717.         MoveWindow(ConnData->SendWindow,
  718.                    0,
  719.                    0,
  720.                    LOWORD(LParam),
  721.                    HIWORD(LParam)/2,
  722.                    TRUE);
  723.  
  724.         MoveWindow(ConnData->RecvWindow,
  725.                    0,
  726.                    HIWORD(LParam)/2 + 1,
  727.                    LOWORD(LParam),
  728.                    HIWORD(LParam) - HIWORD(LParam)/2 - 1,
  729.                    TRUE);
  730.  
  731.         return (DefMDIChildProc(WindowHandle,
  732.                                 Message,
  733.                                 WParam,
  734.                                 LParam));
  735.  
  736.     case USMSG_CONNECT:
  737.  
  738.         // Handle the new connection.
  739.         HandleConnectMessage(WindowHandle, LParam);
  740.         return(0);
  741.  
  742.     case WM_DESTROY:
  743.  
  744.         ConnData = GetConnData(WindowHandle);
  745.         CleanupConnection(ConnData);
  746.  
  747.         return(DefMDIChildProc(WindowHandle,
  748.                                Message,
  749.                                WParam,
  750.                                LParam));
  751.  
  752.     } // switch (Message)
  753.  
  754.     // Pass unprocessed message to DefMDIChildProc.
  755.     return(DefMDIChildProc(WindowHandle,
  756.                            Message,
  757.                            WParam,
  758.                            LParam));
  759.  
  760. } // ConnWndProc()
  761.  
  762.  
  763.  
  764.  
  765.  
  766. long CALLBACK
  767. SendEditSubClass(
  768.     IN HWND   WindowHandle,
  769.     IN UINT   Message,
  770.     IN WPARAM WParam,
  771.     IN LPARAM LParam)
  772.  
  773. /*++
  774.  
  775. Routine Description:
  776.  
  777.     Window procedure used to subclass the edit control for the sending
  778.     window.
  779.  
  780. Implementation:
  781.  
  782.     Text is only allowed to be appended to or deleted from the end of
  783.     an edit control's text buffer.  When text is either typed or
  784.     paste, Chat creates an output request data structure and sticks it
  785.     on the output thread for this connection.
  786.  
  787. Arguments:
  788.  
  789.     WindowHandle - Supplies the handle to the edit control's window
  790.  
  791.     Message - Supplies the message identifier.
  792.  
  793.     WParam - Supplies the first message parameter.
  794.  
  795.     LParam - Supplies the second message parameter.
  796.  
  797. Return Value:
  798.  
  799.     ReturnValue - Depends of the message sent.
  800.  
  801. --*/
  802. {
  803.     int             Index;        // an index into the edit control's text
  804.     HANDLE          CBDataHandle; // handle to the clipboard data
  805.     LPSTR           CBDataPtr;    // pointer to data to send
  806.     PCONNDATA       ConnData;     // pointer to connection-specific data
  807.     POUTPUT_REQUEST OutReq;       // output request data
  808.     int             TextSize;     // number of bytes of text to send
  809.     DWORD           BytesLeft;    // number of bytes left to send
  810.  
  811.     ConnData = GetConnData(GetParent(WindowHandle));
  812.  
  813.     switch (Message) {
  814.  
  815.     case WM_CHAR:
  816.  
  817.         // If the character is sendable, enqueue the character on the
  818.         // output queue, and signal the output event.
  819.         if (IsSendable((char)WParam)) {
  820.  
  821.             OutReq = (POUTPUT_REQUEST) malloc(sizeof(OUTPUT_REQUEST));
  822.             if (OutReq == NULL) {
  823.                 ChatSysError("malloc()",
  824.                              "SendEditSubClass()",
  825.                              TRUE);
  826.             }
  827.  
  828.             // Rather than malloc a one-character buffer, just point
  829.             // Buffer into the already-allocated Char field of the
  830.             // OutReq structure.  This gets rid of a heavy-weight
  831.             // malloc for each character typed.
  832.             OutReq->Buffer.len = sizeof(char);
  833.             OutReq->Buffer.buf = &OutReq->Character;
  834.  
  835.             // Fill in the OutReq structure.
  836.             *(OutReq->Buffer.buf) = (char)WParam;
  837.             OutReq->Type = NON_OVERLAPPED_IO;
  838.             OutReq->ConnData = ConnData;
  839.  
  840.             QInsert(ConnData->OutputQueue, (LPVOID)OutReq);
  841.             SetEvent(ConnData->OutputEventObject);
  842.         }
  843.  
  844.         // Move the cursor to the end of the text buffer.
  845.         Index = GetWindowTextLength(WindowHandle);
  846.         CallWindowProc((WNDPROC)OldEditWndProc,
  847.                        WindowHandle,
  848.                        EM_SETSEL,
  849.                        Index,
  850.                        Index);
  851.  
  852.         // Make room for the new text in the edit control.
  853.         MakeRoom(WindowHandle, 1);
  854.  
  855.         // Pass on the message to the original Edit Window Procedure.
  856.         return (CallWindowProc((WNDPROC)OldEditWndProc,
  857.                                WindowHandle,
  858.                                Message,
  859.                                WParam,
  860.                                LParam));
  861.  
  862.     case WM_PASTE:
  863.  
  864.         // Get the buffer from the edit control and send it over the
  865.         // socket.
  866.         if (IsClipboardFormatAvailable(CF_TEXT)) {
  867.  
  868.             if (OpenClipboard(WindowHandle)) {
  869.                 CBDataHandle = GetClipboardData(CF_TEXT);
  870.  
  871.                 if (CBDataHandle) {
  872.  
  873.                     CBDataPtr = GlobalLock(CBDataHandle);
  874.                     TextSize = strlen(CBDataPtr);
  875.  
  876.                     // Make room in the edit control for the new text; if
  877.                     // this is impossible, just return because MakeRoom
  878.                     // notifies the user internally.
  879.                     if (!MakeRoom(WindowHandle, TextSize)) {
  880.                         GlobalUnlock(CBDataHandle);
  881.                         CloseClipboard();
  882.                         return(0);
  883.                     }
  884.  
  885.                     // If the text is longer than the maximum message
  886.                     // size, we need to split it into bite-size pieces
  887.                     // for consumption by the transport layer.
  888.                     BytesLeft = TextSize;
  889.                     while (BytesLeft != 0) {
  890.  
  891.                         // Create a new output request.
  892.                         OutReq =
  893.                           (POUTPUT_REQUEST)malloc(sizeof(OUTPUT_REQUEST));
  894.                         if (OutReq == NULL) {
  895.                             ChatSysError("malloc()",
  896.                                          "SendEditSubClass",
  897.                                          TRUE);
  898.                         }
  899.  
  900.                         // Determine how much data to send with this
  901.                         // request, and allocate a buffer that size.
  902.                         if (BytesLeft > ConnData->MaxMsgSize) {
  903.                             OutReq->Buffer.len = ConnData->MaxMsgSize;
  904.                         } else {
  905.                             OutReq->Buffer.len = BytesLeft;
  906.                         }
  907.                         OutReq->Buffer.buf =
  908.                           (char *)malloc(OutReq->Buffer.len);
  909.                         if (OutReq->Buffer.buf == NULL) {
  910.                             ChatSysError("malloc()",
  911.                                          "SendEditSubClass",
  912.                                          TRUE);
  913.                         }
  914.  
  915.                         // Fill in the rest of the OutReq structure
  916.                         // and put it in the output queue.
  917.                         OutReq->Type = OVERLAPPED_IO;
  918.                         OutReq->ConnData = ConnData;
  919.                         memcpy(OutReq->Buffer.buf, CBDataPtr,
  920.                                OutReq->Buffer.len);
  921.                         QInsert(ConnData->OutputQueue, (LPVOID)OutReq);
  922.  
  923.                         // Update the number of bytes left to send and
  924.                         // the pointer to the rest of the data.
  925.                         BytesLeft -= OutReq->Buffer.len;
  926.                         CBDataPtr += OutReq->Buffer.len;
  927.                     }
  928.  
  929.                     // Signal the ouput event.
  930.                     SetEvent(ConnData->OutputEventObject);
  931.                     GlobalUnlock(CBDataHandle);
  932.  
  933.                 } else {
  934.  
  935.                     MessageBox(WindowHandle,
  936.                                "Sorry, couldn't retrieve the clipoard data.",
  937.                                "Error.",
  938.                                MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  939.                 }
  940.  
  941.                 CloseClipboard();
  942.  
  943.             } // if (OpenClipboard(WindowHandle))
  944.             else {
  945.  
  946.                 MessageBox(WindowHandle,
  947.                            "Couldn't open the clipboard!!",
  948.                            "Try again.",
  949.                            MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  950.             }
  951.  
  952.         } // if (IsClipboardFormatAvailable(CF_TEXT))
  953.         else {
  954.  
  955.             MessageBox(WindowHandle,
  956.                        "Sorry, the CF_TEXT format not available.",
  957.                        "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  958.         }
  959.         // Move the cursor to the end of the text buffer.
  960.         Index = GetWindowTextLength(WindowHandle);
  961.         CallWindowProc((WNDPROC)OldEditWndProc,
  962.                        WindowHandle,
  963.                        EM_SETSEL,
  964.                        Index,
  965.                        Index);
  966.  
  967.         // Call the real edit control window procedure.
  968.         return (CallWindowProc((WNDPROC)OldEditWndProc,
  969.                                WindowHandle,
  970.                                Message,
  971.                                WParam,
  972.                                LParam));
  973.  
  974.     case WM_KEYDOWN:
  975.  
  976.         // Capture the delete key. nVirtKey: 46 = 'Del'.
  977.         if (WParam == 46) {
  978.             return(0);
  979.         } else {
  980.             return (CallWindowProc((WNDPROC)OldEditWndProc, WindowHandle,
  981.                                    Message, WParam, LParam));
  982.         }
  983.  
  984.     case WM_CUT:  // Flow through
  985.     case WM_UNDO:
  986.  
  987.         return (0);
  988.  
  989.     default:
  990.  
  991.         return (CallWindowProc((WNDPROC)OldEditWndProc,
  992.                                WindowHandle,
  993.                                Message,
  994.                                WParam,
  995.                                LParam));
  996.  
  997.     } // switch (Message)
  998.  
  999. } // SendEditSubClass()
  1000.  
  1001.  
  1002.  
  1003.  
  1004.  
  1005. long CALLBACK
  1006. RecvEditSubClass(
  1007.     IN HWND   WindowHandle,
  1008.     IN UINT   Message,
  1009.     IN WPARAM WParam,
  1010.     IN LPARAM LParam)
  1011.  
  1012. /*++
  1013.  
  1014. Routine Description:
  1015.  
  1016.     Window procedure used to subclass the edit control for the
  1017.     receiving window.
  1018.  
  1019. Implementation:
  1020.  
  1021.     The user cannot modify any of the text displayed in the receiving
  1022.     edit control. All text received from the socket associated with
  1023.     this edit control is displayed.
  1024.  
  1025. Arguments:
  1026.  
  1027.     WindowHandle - Supplies the handle to the edit control's window
  1028.  
  1029.     Message - Supplies the message identifier.
  1030.  
  1031.     WParam - Supplies the first message parameter.
  1032.  
  1033.     LParam - Supplies the second message parameter.
  1034.  
  1035. Return Value:
  1036.  
  1037.     ReturnValue - Depends of the message sent.
  1038.  
  1039. --*/
  1040. {
  1041.     switch (Message) {
  1042.  
  1043.     case WM_CHAR:       // Flow through
  1044.     case WM_KEYDOWN:    // Flow through
  1045.     case WM_UNDO:       // Flow through
  1046.     case WM_PASTE:      // Flow through
  1047.     case WM_CUT:
  1048.  
  1049.         return (0);     // Effectively disables the above messages.
  1050.  
  1051.     default:
  1052.  
  1053.         return (CallWindowProc((WNDPROC)OldEditWndProc,
  1054.                                WindowHandle,
  1055.                                Message,
  1056.                                WParam,
  1057.                                LParam));
  1058.     }
  1059.  
  1060. } // RecvEditSubClass()
  1061.  
  1062.  
  1063.  
  1064.  
  1065.  
  1066. BOOL CALLBACK
  1067. CloseEnumProc(
  1068.     IN HWND WindowHandle,
  1069.     IN LONG LParam)
  1070.  
  1071. /*++
  1072.  
  1073. Routine Description:
  1074.  
  1075.     A callback function used to destroy all of chat's connection
  1076.     windows.
  1077.  
  1078. Arguments:
  1079.  
  1080.     WindowHandle - Supplies the handle to the child's window which
  1081.                    is to be closed.
  1082.  
  1083.     LParam - Not Used.
  1084.  
  1085. Return Value:
  1086.  
  1087.     TRUE - Returns true in order to continue enumeration.
  1088.  
  1089. --*/
  1090. {
  1091.     if (!GetWindow(WindowHandle, GW_OWNER)) {
  1092.  
  1093.         SendMessage(GetParent(WindowHandle), WM_MDIRESTORE,
  1094.                     (WPARAM)WindowHandle, 0);
  1095.  
  1096.         if (SendMessage(WindowHandle, WM_QUERYENDSESSION, 0, 0)) {
  1097.             SendMessage(GetParent(WindowHandle), WM_MDIDESTROY,
  1098.                         (WPARAM)WindowHandle, 0);
  1099.         }
  1100.     }
  1101.     return (TRUE);
  1102.  
  1103. } // CloseEnumProc()
  1104.  
  1105.  
  1106.  
  1107.  
  1108.  
  1109. void
  1110. OutputString(
  1111.     IN HWND RecvWindow,
  1112.     IN char *String)
  1113. /*++
  1114.  
  1115. Routine Description:
  1116.  
  1117.     This function sends the string received over the socket to the
  1118.     edit window given by RecvWindow.
  1119.  
  1120. Implementation:
  1121.  
  1122.     This function treats certain non-printable characters specially so
  1123.     that they mean the same thing on this end as they did on the end
  1124.     that sent them.
  1125.  
  1126. Arguments:
  1127.  
  1128.     RecvWindow -- Handle to the edit control window which is to
  1129.     receive the text.
  1130.  
  1131.     String -- Points to the NULL-terminated string to be output.
  1132.  
  1133. Return Value:
  1134.  
  1135.     None.
  1136.  
  1137. --*/
  1138. {
  1139.  
  1140.     int  Index;         // indices into the edit control text
  1141.     char *Current = String;     // the character we are examining
  1142.     char *First = String;       // the first character we have not output
  1143.     int  NumChars;              // how many characters we put in Buffer
  1144.  
  1145.     // Make room for the string in the edit control.
  1146.     MakeRoom(RecvWindow, strlen(String));
  1147.  
  1148.     // This loop goes through the whole string we have been instructed
  1149.     // to print in the edit control; when it encounters certain
  1150.     // characters in the string, it sends certain messages to the edit
  1151.     // control to simulate the right behavior.
  1152.     while (*Current) {
  1153.  
  1154.         switch (*Current) {
  1155.  
  1156.         case '\b':
  1157.  
  1158.             // The current character is a backspace.  Print out the
  1159.             // string we have so far, and then manually erase a
  1160.             // character.
  1161.             *Current = '\0';
  1162.  
  1163.             // Output the string at 'First'...
  1164.             Index = GetWindowTextLength(RecvWindow);
  1165.             SendMessage(RecvWindow, EM_SETSEL, Index, Index);
  1166.             SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);
  1167.  
  1168.             // Get the number of chars in the last line.
  1169.             Index = GetWindowTextLength(RecvWindow);
  1170.             NumChars = SendMessage(RecvWindow, EM_LINELENGTH, Index, 0);
  1171.  
  1172.             // If the line had no characters in it, then it's the
  1173.             // beginning of a new line, and we should erase two
  1174.             // characters (the preceding \r\n) rather than just one.
  1175.             if (NumChars == 0) {
  1176.                 SendMessage(RecvWindow, EM_SETSEL, Index - 2, Index);
  1177.                 SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"");
  1178.             } else {
  1179.                 SendMessage(RecvWindow, EM_SETSEL, Index - 1, Index);
  1180.                 SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"");
  1181.             }
  1182.  
  1183.             First = Current + 1;
  1184.             break;
  1185.  
  1186.         case '\r':
  1187.  
  1188.             // A newline.  If the Chat window is minimized, ask the
  1189.             // user if they want it maximized.
  1190.             QueryAndMaximize();
  1191.  
  1192.             // If a remote user typed a newline, we'll receive '\r',
  1193.             // but if the remote user pasted it in, we'll get '\r\n'.
  1194.             if (*(Current + 1) != '\n') {
  1195.  
  1196.                 // The newline was typed in...output the string up to
  1197.                 // the newline, then "\r\n".
  1198.                 *Current = '\0';
  1199.  
  1200.                 Index = GetWindowTextLength(RecvWindow);
  1201.                 SendMessage(RecvWindow, EM_SETSEL, Index, Index);
  1202.                 SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);
  1203.  
  1204.                 Index = GetWindowTextLength(RecvWindow);
  1205.                 SendMessage(RecvWindow, EM_SETSEL, Index, Index);
  1206.                 SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"\r\n");
  1207.  
  1208.                 First = Current + 1;
  1209.             }
  1210.  
  1211.             break;
  1212.  
  1213.         default:
  1214.  
  1215.             break;
  1216.  
  1217.         } // switch (*Current)
  1218.         Current++;
  1219.  
  1220.     } // while (*Current)
  1221.  
  1222.     // Ouptut any string leftover in First
  1223.     Index = GetWindowTextLength(RecvWindow);
  1224.     SendMessage(RecvWindow, EM_SETSEL, Index, Index);
  1225.     SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);
  1226. }
  1227.  
  1228.  
  1229.  
  1230.  
  1231.  
  1232. void
  1233. QueryAndMaximize(void)
  1234. /*++
  1235.  
  1236. Routine Description:
  1237.  
  1238.     If Chat's main frame window is minimized, this function asks the
  1239.     user if they want to activate and display Chat, and acts
  1240.     accordingly.
  1241.  
  1242. Arguments:
  1243.  
  1244.     None.
  1245.  
  1246. Return Value:
  1247.  
  1248.     None.
  1249.  
  1250. --*/
  1251. {
  1252.     WINDOWPLACEMENT WinPlace; // holds window placement data
  1253.  
  1254.     WinPlace.length = sizeof(WINDOWPLACEMENT);
  1255.  
  1256.     // Get chat's main window's state.
  1257.     GetWindowPlacement(GlobalFrameWindow, &WinPlace);
  1258.  
  1259.     // If it's minimized, ask the user if they want to maximize.
  1260.     if (WinPlace.showCmd == SW_SHOWMINIMIZED) {
  1261.  
  1262.         if (MessageBox(GlobalFrameWindow,
  1263.                        "There is activity on a Chat connection.  Maximize?",
  1264.                        "Chat has data.",
  1265.                        MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND)
  1266.             == IDYES) {
  1267.  
  1268.             // Maximize chat's main window.
  1269.             WinPlace.showCmd = SW_RESTORE;
  1270.             SetWindowPlacement(GlobalFrameWindow, &WinPlace);
  1271.         }
  1272.     }
  1273. } // QueryAndMaximize()
  1274.  
  1275.  
  1276.  
  1277.  
  1278.  
  1279. BOOL
  1280. GetConnectionInfo(
  1281.     IN HWND ConnectionWindow)
  1282.  
  1283. /*++
  1284.  
  1285. Routine Description:
  1286.  
  1287.     Queries the user as to what sort of connection (i.e. what address
  1288.     family/protocol) she wants to make.
  1289.  
  1290. Implementation:
  1291.  
  1292.     After popping up a listbox, filling it in with all the available
  1293.     protocol choices, and getting the user's choice, this function
  1294.     uses it to determine what kind of dialog box to pop up to get the
  1295.     actual connection parameters, which are stored in the CONNDATA
  1296.     associated with the given ConnectionWindow.
  1297.  
  1298. Arguments:
  1299.  
  1300.     ConnectionWindow -- Handle to the connection window associated
  1301.     with this connection.
  1302.  
  1303. Return Value:
  1304.  
  1305.     TRUE - The user closed a dialog box by clicking the 'Ok' button.
  1306.  
  1307.     FALSE - The user closed a dialog box by clicking the 'Cancel'
  1308.             button.
  1309.  
  1310. --*/
  1311.  
  1312. {
  1313.     PCONNDATA ConnData;           // connection-specific data
  1314.     BOOL      ReturnValue = TRUE; // holds the return value
  1315.     struct sockaddr_atm * pATMSockAddr;
  1316.  
  1317.     ConnData = GetConnData(ConnectionWindow);
  1318.  
  1319.     // Pop up a dialog box from which the user chooses the protocol; a
  1320.     // pointer to a protocol info struct is stored in ConnData.
  1321.     if (!DialogBoxParam(GlobalInstance,
  1322.                         "ChooseFamilyDlg",
  1323.                         ConnectionWindow,
  1324.                         ChooseFamilyDlgProc,
  1325.                         (LPARAM)ConnData)) {
  1326.         ReturnValue = FALSE;
  1327.         goto Done;
  1328.     }
  1329.  
  1330.     // The user has selected a protocol; now pop up an address family
  1331.     // specific dialog box to get the address to which we want to make
  1332.     // a chat connection, a pointer to which is stored in
  1333.     // ConnData->SockAddr (see InetConnDlg, for example).
  1334.     switch (ConnData->ProtocolInfo->iAddressFamily) {
  1335.  
  1336.         case AF_INET:
  1337.  
  1338.             ReturnValue = DialogBoxParam(GlobalInstance,
  1339.                                          "InetConnDlg",
  1340.                                          ConnectionWindow,
  1341.                                          InetConnDlgProc,
  1342.                                          (LPARAM)ConnData);
  1343.             goto Done;
  1344.  
  1345.         case AF_ATM:
  1346.             ConnData->RemoteSockAddr.len = sizeof(struct sockaddr_atm);
  1347.             ConnData->RemoteSockAddr.buf =
  1348.                         (char FAR *)malloc(ConnData->RemoteSockAddr.len);
  1349.  
  1350.             pATMSockAddr = (struct sockaddr_atm *)ConnData->RemoteSockAddr.buf;
  1351.             pATMSockAddr->satm_number.AddressType  = ATM_NSAP;
  1352.             pATMSockAddr->satm_blli.Layer2Protocol = SAP_FIELD_ABSENT;
  1353.             pATMSockAddr->satm_blli.Layer3Protocol = SAP_FIELD_ABSENT;
  1354.             pATMSockAddr->satm_bhli.HighLayerInfoType = BHLI_UserSpecific;
  1355.  
  1356.             ReturnValue = DialogBoxParam(GlobalInstance,
  1357.                                          "ATMSOCKADDRDLG",
  1358.                                          ConnectionWindow,
  1359.                                          ATMSockAddrProc,
  1360.                                          (LPARAM)pATMSockAddr);
  1361.             goto Done;
  1362.  
  1363.         default:
  1364.  
  1365.             ReturnValue = DialogBoxParam(GlobalInstance,
  1366.                                    "DefaultConnDlg",
  1367.                                    ConnectionWindow,
  1368.                                    DefaultConnDlgProc,
  1369.                                    (LPARAM)ConnData);
  1370.             goto Done;
  1371.     }
  1372.  
  1373.  Done:
  1374.  
  1375.     return(ReturnValue);
  1376.  
  1377. } // GetConnectionInfo()
  1378.  
  1379.  
  1380.  
  1381.  
  1382.  
  1383. BOOL
  1384. TranslateHex(
  1385.     OUT LPVOID Buffer,
  1386.     IN  int    BufferLen,
  1387.     IN  char   *HexString,
  1388.     IN  HWND   WindowHandle)
  1389. /*++
  1390.  
  1391. Routine Description:
  1392.  
  1393.     This function converts an arbitrarily long string of hexidecimal
  1394.     digits into a binary representation.
  1395.  
  1396.     Example -- if the string "0fbf" is passed in, then two bytes will
  1397.     be written into Buffer -- 15 and 191.
  1398.  
  1399. Arguments:
  1400.  
  1401.     Buffer -- Points to a buffer where we want the data stored.
  1402.  
  1403.     BufferLen -- The length, in bytes, of Buffer.
  1404.  
  1405.     HexString -- A null-terminated string of digits with values in the
  1406.     ASCII range of 0-9 and a-f.
  1407.  
  1408.     WindowHandle -- Window handle needed in case we pop up a dialog
  1409.     box.
  1410.  
  1411. Return Value:
  1412.  
  1413.     TRUE -- HexString was successfully translated.
  1414.  
  1415.     FALSE -- One of the characters does not represent a hex digit, or
  1416.     the HexString is too long to be translated into BufferLen bytes.
  1417.  
  1418. --*/
  1419. {
  1420.  
  1421.     int  HexStringLen;       // the length of the HexString, in bytes
  1422.     int  i;                  // counting variable
  1423.     char TwoCharString[3];   // stores two hex characters + a NULL char
  1424.     char *NextByte;          // next free byte in the buffer
  1425.     BOOL ReturnValue = TRUE; // return value
  1426.  
  1427.     HexStringLen  = strlen(HexString);
  1428.     NextByte = (char *)Buffer;
  1429.     TwoCharString[2] = '\0';
  1430.  
  1431.     // Zero out the output buffer.
  1432.     memset(NextByte, 0, BufferLen);
  1433.  
  1434.     // If the hex string in more than twice as long as the buffer, we
  1435.     // don't have a big enough buffer.
  1436.     if ((HexStringLen / 2) > BufferLen) {
  1437.         ReturnValue = FALSE;
  1438.     } else {
  1439.  
  1440.         // Go through the hex string two characters at a time...
  1441.         for (i = 0; i < HexStringLen; i += 2) {
  1442.  
  1443.             // Copy the next two bytes of the hex string into
  1444.             // TwoCharString; then check to make sure they are both
  1445.             // hexidecimal digits.
  1446.             memcpy(TwoCharString, HexString + i, 2);
  1447.             if (!isxdigit(TwoCharString[0]) || !isxdigit(TwoCharString[1])) {
  1448.                 MessageBox(WindowHandle, "Type a hexidecimal number, please",
  1449.                            "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  1450.                 ReturnValue = FALSE;
  1451.                 break;
  1452.             }
  1453.             *NextByte++ = (char)strtol(TwoCharString, NULL, 16);
  1454.  
  1455.         } // for
  1456.     } // else
  1457.     return(ReturnValue);
  1458.  
  1459. } // TranslateHex()
  1460.  
  1461.  
  1462.  
  1463.  
  1464.  
  1465. BOOL
  1466. PackTwoStrings(
  1467.     OUT char  *Buffer,
  1468.     IN  int   BufferLen,
  1469.     IN  char  *String1,
  1470.     IN  char  *String2)
  1471. /*++
  1472.  
  1473. Routine Description:
  1474.  
  1475.     Packs two strings into one buffer.  Useful for packing caller data
  1476.     into a buffer for connection-time data transfer.  It is not an
  1477.     error if either string is of zero length, i.e. just the '\0'
  1478.     character.
  1479.  
  1480. Arguments:
  1481.  
  1482.     Buffer -- Pointer to a buffer into which the two strings will be
  1483.     packed.
  1484.  
  1485.     BufferLen -- The length, in bytes, of Buffer.
  1486.  
  1487.     String1 - Address of the first NULL terminated string.
  1488.  
  1489.     String2 - Address of the second NULL terminated string.
  1490.  
  1491. Return Value:
  1492.  
  1493.     TRUE -- The strings were successfully packed into the buffer.
  1494.  
  1495.     FALSE -- The passed in buffer was NULL, one or both of the strings
  1496.     were NULL, or Buffer is too small.
  1497.  
  1498. --*/
  1499.  
  1500. {
  1501.     int Length1, Length2;    // holds the length of the strings
  1502.     BOOL ReturnValue = TRUE; // holds the return value
  1503.  
  1504.     if (!Buffer || !String1 || !String2) {
  1505.         ReturnValue = FALSE;
  1506.         goto Done;
  1507.     }
  1508.  
  1509.     Length1 = strlen(String1);
  1510.     Length2 = strlen(String2);
  1511.  
  1512.     if (BufferLen < (Length1 + Length2 + 2)) {
  1513.         ReturnValue = FALSE;
  1514.         goto Done;
  1515.     }
  1516.  
  1517.     strcpy(Buffer, String1);
  1518.     strcpy(Buffer + Length1 + 1, String2);
  1519.  
  1520.  Done:
  1521.  
  1522.     return(ReturnValue);
  1523.  
  1524. } // PackTwoStrings()
  1525.  
  1526.  
  1527.  
  1528.  
  1529.  
  1530. BOOL
  1531. ExtractTwoStrings(
  1532.     IN  char *Buffer,
  1533.     OUT char *String1,
  1534.     IN  char Length1,
  1535.     OUT char *String2,
  1536.     IN  int  Length2)
  1537.  
  1538. /*++
  1539.  
  1540. Routine Description:
  1541.  
  1542.     Extracts two strings from a buffer, expecting them to be packed as
  1543.     PackTwoStrings does it.
  1544.  
  1545. Arguments:
  1546.  
  1547.     Buffer -- The buffer which contains two strings.
  1548.  
  1549.     String1 -- A pointer to a buffer of size Length1.
  1550.  
  1551.     Length1 -- The length, in bytes, of String1.
  1552.  
  1553.     String2 -- A pointer to a buffer of size Length2.
  1554.  
  1555.     Length2 -- The length, in bytes, of String2.
  1556.  
  1557. Return Value:
  1558.  
  1559.     TRUE -- The strings were successfully extracted.
  1560.  
  1561.     FALSE -- One of the pointers was NULL, or a packed string was too
  1562.     big to be extracted out into a buffer.
  1563.  
  1564. --*/
  1565.  
  1566. {
  1567.     int BufStrLen1, BufStrLen2; // the length of the packed strings
  1568.     char *BufStr2;              // points to the second packed string
  1569.     BOOL ReturnValue = TRUE;    // holds the return value
  1570.  
  1571.     if (!Buffer || !String1 || !String2) {
  1572.         ReturnValue = FALSE;
  1573.         goto Done;
  1574.     }
  1575.  
  1576.     BufStrLen1 = strlen(Buffer);
  1577.     BufStr2 = Buffer + BufStrLen1 + 1;
  1578.     BufStrLen2 = strlen(BufStr2);
  1579.  
  1580.     if ((BufStrLen1 > Length1) || (BufStrLen2 > Length2)) {
  1581.         ReturnValue = FALSE;
  1582.         goto Done;
  1583.     }
  1584.  
  1585.     strcpy(String1, Buffer);
  1586.     strcpy(String2, BufStr2);
  1587.  
  1588.  Done:
  1589.  
  1590.    return(ReturnValue);
  1591.  
  1592. } // ExtractTwoStrings()
  1593.  
  1594.  
  1595.  
  1596.  
  1597.  
  1598. void
  1599. ChatSysError(
  1600.     IN char *FailedFunction,
  1601.     IN char *InFunction,
  1602.     IN BOOL Fatal)
  1603. /*++
  1604.  
  1605. Routine Description:
  1606.  
  1607.     Pops up a standard message box to display fatal system errors; if
  1608.     the error is fatal, the process is ended and this function does
  1609.     not return.
  1610.  
  1611. Arguments:
  1612.  
  1613.     FailedFunction -- Pointer to a string that contains the name of
  1614.     the system function that failed.
  1615.  
  1616.     InFunction -- Pointer to a string that contains the name of the
  1617.     chat function in which the failure occurred.
  1618.  
  1619.     Fatal -- Boolean indicating whether the error is fatal to the
  1620.     process or not.
  1621.  
  1622. Return Value:
  1623.  
  1624.     None.
  1625.  
  1626. --*/
  1627. {
  1628.  
  1629.     char MsgText[MSG_LEN]; // holds message strings
  1630.  
  1631.     wsprintf(MsgText,
  1632.              "System call %s failed in chat function %s. Error code: %d",
  1633.              FailedFunction, InFunction, GetLastError());
  1634.     MessageBox(GlobalFrameWindow,
  1635.                MsgText,
  1636.                Fatal ? "Fatal Error." : "Non-Fatal Error.",
  1637.                MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  1638.  
  1639.     if (Fatal) {
  1640.         ExitProcess(CHAT_ERROR);
  1641.     }
  1642.  
  1643. } // ChatSysError()
  1644.  
  1645.  
  1646.  
  1647.  
  1648.  
  1649. BOOL
  1650. MakeRoom(
  1651.     IN HWND EditControl,
  1652.     IN int  HowMuch)
  1653. /*++
  1654.  
  1655. Routine Description:
  1656.  
  1657.     This function makes room in an edit control for more text, if
  1658.     necessary.
  1659.  
  1660. Arguments:
  1661.  
  1662.     EditControl -- The edit control in question.
  1663.  
  1664.     HowMuch -- How many characters do we want to put into the edit
  1665.     control?
  1666.  
  1667. Return Value:
  1668.  
  1669.     TRUE -- If there was not enough room in the edit control, MakeRoom
  1670.     successfully chopped off some text from the beginning, and there
  1671.     is now enough room.
  1672.  
  1673.     FALSE -- The requested amount can not fit in the edit control,
  1674.     period; a message box has informed the user.
  1675.  
  1676. --*/
  1677. {
  1678.  
  1679.     int  TextLength;  // how much text is in the edit control
  1680.     int  CharIndex1;
  1681.     int  CharIndex2;
  1682.     int  LineIndex;   // index variables
  1683.     BOOL ReturnValue; // return value
  1684.  
  1685.     if (HowMuch > MAX_EC_TEXT) {
  1686.  
  1687.         MessageBox(EditControl,
  1688.                    "Clipboard too long. Try pasting a smaller amount.",
  1689.                    "Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  1690.         ReturnValue = FALSE;
  1691.  
  1692.     } else {
  1693.  
  1694.         TextLength = GetWindowTextLength(EditControl);
  1695.  
  1696.         if (TextLength + HowMuch > MAX_EC_TEXT) {
  1697.  
  1698.             // Inserting the text would exceed our limit.  This code
  1699.             // chops off roughly the first half of the edit control's
  1700.             // text.
  1701.             CharIndex1 = TextLength / 2;
  1702.             LineIndex = SendMessage(EditControl, EM_LINEFROMCHAR,
  1703.                                     (WPARAM)CharIndex1, 0);
  1704.             CharIndex2 = SendMessage(EditControl, EM_LINEINDEX,
  1705.                                      (WPARAM)LineIndex, 0);
  1706.  
  1707.             SendMessage(EditControl, EM_SETSEL, 0, CharIndex2);
  1708.             SendMessage(EditControl, EM_REPLACESEL, 0,
  1709.                         (LPARAM)"");
  1710.         }
  1711.  
  1712.         ReturnValue = TRUE;
  1713.     } // else
  1714.  
  1715.     return(ReturnValue);
  1716. } // MakeRoom()
  1717.